#import "ToyWin.h"
#import <appkit/Application.h>
#import <appkit/publicWraps.h>
#import <appkit/SavePanel.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/NXEPSImageRep.h>
#import <appkit/Control.h>
#import  <objc/NXBundle.h>	/* LocalizedString */
#import <appkit/Panel.h>
#import <appkit/tiff.h>
#import <streams/streams.h> // NXStream
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <libc.h>
#import "ToyView.h"
#import "TVController.h"
#import "TiffSavePanel.h"
#import "common.h"
#import "save.h"

@implementation ToyWin (ImageSave)

static char *mk_tmpname(const char *s, const char *ex, BOOL nopath)
{
	static char	srcfn[MAXFILENAMELEN];
	int	i, j;

	if (nopath) {
		for (i = j = 0; s[i]; i++)
			if (s[i] == '/') j = i + 1;
	}else
		j = 0;
	strcpy(srcfn, s + j);
	for (i = j = 0; srcfn[i]; i++)
		if (srcfn[i] == '.') j = i + 1;
	if (j == 0)
		srcfn[i] = '.',  j = i + 1;
	while (*ex)
		srcfn[j++] = *ex++;
	srcfn[j] = 0;
	return srcfn;
}


static char saveDir[MAXFILENAMELEN];

- (char *)getSavename: (const char *)path with:(int)itype
{
	SavePanel *savePanel;
	char	*stmp, *sav = NULL;
	const char *ex = NULL;

	switch (itype) {
	case Type_eps: ex = "eps";  break;
	case Type_bmp: ex = "bmp";  break;
	case Type_gif: ex = "gif";  break;
	case Type_ppm: ex = "pnm";  break;
	}
	stmp = mk_tmpname(path, ex, YES);
	if (!saveDir[0])
		strcpy(saveDir, getenv("HOME"));
	savePanel = [SavePanel new];
	if ([savePanel runModalForDirectory: saveDir file: stmp]) {
		sav = mk_tmpname([savePanel filename], ex, NO);
		if (sav == NULL || *sav == 0)
			return NULL;
		strcpy(saveDir, [savePanel directory]);
	}
	return sav;
}

- (char *)getSaveTiffname: (const char *)path jpeg: (BOOL)flag
	compress: (int *)type by: (float *)factor
{
	TiffSavePanel *savePanel;
	char	*stmp, *sav = NULL;
	static	float jpegFactor = 10.0; /* Default */

	stmp = mk_tmpname(path, "tiff", YES);
	if (!saveDir[0])
		strcpy(saveDir, getenv("HOME"));
	savePanel = [[TiffSavePanel new] init: jpegFactor jpeg: flag];
	if ([savePanel runModalForDirectory: saveDir file: stmp]) {
		sav = mk_tmpname([savePanel filename], "tiff", NO);
		if (sav == NULL || *sav == 0)
			return NULL;
		strcpy(saveDir, [savePanel directory]);
		[savePanel compressType: type with: factor];
		if (*type == NX_TIFF_COMPRESSION_JPEG)
			jpegFactor = *factor;
	}
	return sav;
}

- saveAsTiff: sender
{
	NXStream *stream;
	int	fd = -1;
	char	*sav;
	commonInfo *cinf;
	NXBitmapImageRep *rep;
	BOOL	jflag;
	int	type;
	float	factor;

	cinf = [[self toyView] commonInfo];
	rep = (NXBitmapImageRep *)[[[self toyView] image] bestRepresentation];
	jflag = (cinf->bits > 2  /* && cinf->type != Type_eps */
		&& [rep canBeCompressedUsing: NX_TIFF_COMPRESSION_JPEG]);
	sav = [self getSaveTiffname: imageFilename jpeg: jflag
		compress: &type by: &factor];
	if (sav == NULL) /* canceled */
		return self;
	if ((fd = open(sav, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0
	|| (stream = NXOpenFile(fd, NX_WRITEONLY)) == NULL) {
		if (fd >= 0) (void) close(fd);
		errAlert(sav, Err_SAVE);
		return self;
	}
	if (factor < 1.0) factor = 1.0;
	else if (factor > 255.0) factor = 255.0;
	[rep writeTIFF:stream usingCompression:type andFactor:factor];
	NXClose(stream);
	(void)close(fd);
	return self;
}

- saveAsEPS: sender
{
	NXStream *stream;
	char	*sav;
	id	tv;
	NXRect	rect;

	sav = [self getSavename: imageFilename with:Type_eps];
	if (sav == NULL) /* canceled */
		return self;
	stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
	if (stream == NULL) {
		errAlert(sav, Err_MEMORY);
		return self;
	}
	tv = [self toyView];
	[tv getFrame:&rect];
	[tv copyPSCodeInside:&rect to:stream];
	NXFlush(stream);
	if (NXSaveToFile(stream, sav))
		errAlert(sav, Err_SAVE);
	NXCloseMemory(stream, NX_FREEBUFFER);
	return self;
}


/* Local Method */
- saveFullColor:(FILE *)fp info:(commonInfo *)cinf
	colors:(int)cnum type:(int)itype map:(unsigned char **)map
{
	const char *ask, *full, *reduce;
	paltype *pal = NULL;
	char msg[64];
	float bright;

	if (itype == Type_bmp) {
		ask = NXLocalizedString("Reduce Color?", NULL, BMP_Reduction);
		full = NXLocalizedString("Leave", NULL, BMP_Leave);
		reduce = NXLocalizedString("Reduce", NULL, BMP_Reduce);
		if (NXRunAlertPanel("", ask, full, reduce, NULL)) {
			saveBmpbmap(fp, cinf, cnum, NULL, map);
			return self;
		}
	}else /* gif */ {
		ask = NXLocalizedString("Reduction Start",
				NULL, GIF_Reduction);
		(void) NXRunAlertPanel("", ask, NULL, NULL, NULL);
	}

	[messagePanel makeKeyAndOrderFront: self];
	[messageText setStringValue:"Trying Reduction..."];
	[messagePanel setFloatingPanel: YES];
	NXPing();
	for ( bright = 0.0; ; ) {
		if (reduce_bright(&cnum, &bright, map)) break;
		sprintf(msg, "Trying Reduction: Brightness %d%%",
			(int)(bright * 100.0));
		[messageText setStringValue: msg];
		NXPing();
	}
	[messageText setStringValue: "Making Palette..."];
	NXPing();
	pal = get256map(&cnum);
	[messageText setStringValue: "Writing Image..."];
	NXPing();
	if (itype == Type_bmp)
		saveBmpbmap(fp, cinf, cnum, pal, map);
	else {
		resetPixel(map, 0);
		GIFEncode(fp, cinf, cnum, pal);
	}
	[messagePanel setFloatingPanel: NO];
	[messagePanel close];

	return self;
}


- (int)getBitmap:(unsigned char **)map info:(commonInfo **)infp
{
	NXImageRep *rep;
	rep = [[[self toyView] image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	return 0;
}

- freeTempBitmap
{
	return self;
}

- saveAs: (int)itype
{
	FILE	*fp;
	char	*sav;
	commonInfo *cinf;
	unsigned char *map[5];
	int	cnum, err;

	cinf = [[self toyView] commonInfo];
	if (cinf->cspace == NX_CMYKColorSpace) {
		errAlert(imageFilename, Err_SAV_IMPL);
		return self;
	}

	sav = [self getSavename: imageFilename with: itype];
	if (sav == NULL) /* canceled */
		return self;
	if ((fp = fopen(sav, "w")) == NULL) {
		errAlert(sav, Err_SAVE);
		return self;
	}

	err = [self getBitmap:map info: &cinf];

	if (err == 0 && (err = initGetPixel(cinf)) == 0) {
		if (itype == Type_ppm) {
			resetPixel(map, 0);
			if ((err = ppmwrite(fp, cinf, map)) == 0)
				goto EXIT;
		}else {
			if (cinf->palette) {
				err = getAllPalColor();
				cnum = cinf->palsteps;
			}else
				err = getAllColor(&cnum, map);
		}
	}
	if (err) {
		errAlert(sav, err);
		(void)fclose(fp);
		(void)unlink(sav);
		return self;
	}

	if (cnum > FIXcount) /* bmp / gif */
		[self saveFullColor: fp
			info: cinf colors: cnum type: itype map: map];
	else {
		paltype *pal = cinf->palette;
		if (pal == NULL)
			pal = getNormalmap(&cnum);
		if (itype == Type_bmp)
			saveBmpbmap(fp, cinf, cnum, pal, map);
		else {
			resetPixel(map, 0);
			GIFEncode(fp, cinf, cnum, pal);
		}
	}
EXIT:
	free256map();
	(void)fclose(fp);
	[self freeTempBitmap];
	return self;
}

- print: sender
{
	[[self toyView] printPSCode:sender];
	return self;
}

@end
